#include "process.h"

void set_signals_callbacks(void)
{
    struct sigaction act; 	/* Signal handler structure. */
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = signal_callback;
    sigaction(SIGINT, &act, NULL);    /* SIGINT  */
    sigaction(SIGTSTP, &act, NULL);   /* SIGTSTP */
    sigaction(SIGCHLD, &act, NULL);   /* SIGCHLD */
}

void signal_callback(int signal)
{
	if(signal == SIGCHLD)
		process_remove_dead();
	else
		process_kill_fg(signal);
}

void process_insert(pid_t pid, int ground)
{
	process *p = (process *)malloc(sizeof(process));
	p->pid = pid;
	p->ground = ground;
	p->status = RUNNING;
	ll_insert_first(process_list, p);
}

void process_remove(pid_t pid)
{
	ll_iterator *aux = ll_get_iterator(process_list);
	process *p;

	while(!ll_is_end(aux))
	{
		p = (process *) ll_get(aux);
		if(p->pid == pid)
		{
			ll_remove(aux, free);
			break;
		}
		ll_next(aux);
	}
	free(aux);
}

void process_kill_fg(int signal)
{
	process *p = get_fg_process();
	if(signal == SIGTSTP) p->status = STOPPED;
	kill(p->pid, signal);
	printf("\n");
}

process *get_process(pid_t pid)
{
	ll_iterator *aux = ll_get_iterator(process_list);
	process *p;

	while(!ll_is_end(aux))
	{
		p = (process *) ll_get(aux);
		if(p->pid == pid) break;
		else p = NULL;
		ll_next(aux);
	}
	free(aux);
	return p;
}

process *get_fg_process()
{
	ll_iterator *aux = ll_get_iterator(process_list);
	process *p;

	while(!ll_is_end(aux))
	{
		p = (process *)ll_get(aux);
		if(p->ground == FG) break;
		else p = NULL;
		ll_next(aux);
	}
	free(aux);
	return p;
}

void process_remove_dead()
{
	int status;
	pid_t pid = -1;
	process *p;
	char what[100];

	pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);

	if(pid == -1 || pid == 0)
	{
		/* -1 : é o FG process, quem se preocupa é o external_cmd::wait_child() */
		/*  0 : é o calling process (shell) */
		return;
	}

	/* Qualquer filho perdido pelo mundo... */
	p = get_process(pid);

	if(WIFEXITED(status))
	{
	   sprintf(what, "[exited with status %d] ", WEXITSTATUS(status));
	   process_remove(pid);
	}
	else if(WIFSIGNALED(status))
	{
	   sprintf(what, "[killed by signal %d] ", WTERMSIG(status));
	   process_remove(pid);
	}
	else if(WIFSTOPPED(status))
	{
	   sprintf(what, "[stopped by signal %d] ", WSTOPSIG(status));
       p->status = STOPPED;
	}
	else if(WIFCONTINUED(status))
	{
	   sprintf(what, "[continued] ");
       p->status = CONTINUED;
	}

	sprintf(what, "%s %d\n", what, pid);
	str_cats(msg, what);
}

